import { isObjectCharacter } from "@/utils/ImageFileType.js";
import { config } from "@/managers/ConfigManager.js";
import { blendmodes } from '@/const/blendmodes.js';
import EventEmitter from "eventemitter3";

export class Game_CharacterBase extends EventEmitter {
    constructor() {
        super();
        this._x = 0;
        this._y = 0;
        this._realX = 0;
        this._realY = 0;
        this._moveSpeed = 4;
        this._moveFrequency = 6;
        this._opacity = 255;
        this._blendMode = 0;
        this._direction = 2;
        this._pattern = 1;
        this._priorityType = 1;
        this._tileId = 0;
        this._characterName = '';
        this._characterIndex = 0;
        this._isObjectCharacter = false;
        this._walkAnime = true;
        this._stepAnime = false;
        this._directionFix = false;
        this._through = false;
        this._transparent = false;
        this._bushDepth = 0;
        this._animationId = 0;
        this._balloonId = 0;
        this._animationPlaying = false;
        this._balloonPlaying = false;
        this._animationCount = 0;
        this._stopCount = 0;
        this._jumpCount = 0;
        this._jumpPeak = 0;
        this._movementSuccess = true;

        this._distancePerFrame=this.distancePerFrame();
    }

    get x() { return this._x; }
    get y() { return this._y; }

    pos(x, y) {
        return this._x == x && this._y == y;
    }

    posNt(x, y) {
        // No through
        return this.pos(x, y) && !this.isThrough();
    }

    moveSpeed() {
        return this._moveSpeed;
    }

    setMoveSpeed(moveSpeed) {
        this._moveSpeed = moveSpeed;
    }

    moveFrequency() {
        return this._moveFrequency;
    }

    setMoveFrequency(moveFrequency) {
        this._moveFrequency = moveFrequency;
    }

    setPattern(pattern) {
        if(this._pattern!=pattern){
            this._pattern = pattern;
            this.emit('character:bus','pattern');
        }
    }

    setDirection(d) {
        if (!this.isDirectionFixed()&&d&&this._direction!=d) {
            this._direction = d;
            this.emit('character:bus','direction');
        }
        this.resetStopCount();
    }

    setOpacity(opacity) {
        this._opacity = opacity;
        this.emit('character:bus','opacity',opacity);
    }

    setBushDepth(value){
        if(this._bushDepth!=value){
            this._bushDepth=value;
            this.emit('character:bus','bushDepth',value);
        }
    }

    setBlendMode(blendMode) {
        if(this._blendMode!=blendMode){
            this._blendMode = blendMode;
            this.emit('character:bus','blendMode',this.blendMode());
        }
    }

    setPriorityType(priorityType) {
        this._priorityType = priorityType;
        this.emit('character:bus','priority',this.screenZ());
    }

    setTransparent(transparent) {
        this._transparent = transparent;
        this.emit('character:bus','transparent',transparent);
    }

    setImage(characterName, characterIndex) {
        this._tileId = 0;
        if(this._characterName!=characterName){
            this._characterName = characterName;
            this._isObjectCharacter = isObjectCharacter(characterName);
            this.emit('character:bus','characterName',characterName);
        }
        if(this._characterIndex!=characterIndex){
            this._characterIndex = characterIndex;
            this.emit('character:bus','characterIndex',characterIndex);
        }
    }

    setTileImage(tileId) {
        if(this._tileId!=tileId){
            this._tileId = tileId;
            this._isObjectCharacter = true;
            this.emit('character:bus','tile',tileId);
        }
        this._characterName = '';
        this._characterIndex = 0;
    }

    requestAnimation(animationId) {
        this._animationId = animationId;
        this.emit('character:bus','animation',animationId);
    }

    requestBalloon(balloonId) {
        this._balloonId = balloonId;
        this.emit('character:bus','balloon',balloonId);
    }

    setRealPosition(x,y){
        if(this._realX!=x||this._realY!=y){
            this._realX=x;
            this._realY=y;
            this.emit('character:bus','move');
        }
    }

    setPosition(x, y) {
        this._x = Math.round(x);
        this._y = Math.round(y);
        this.setRealPosition(x,y);
    }

    opacity() {
        return this._opacity;
    }

    blendMode() {
        return blendmodes[this._blendMode];
    }

    isNormalPriority() {
        return this._priorityType == 1;
    }

    isMoving() {
        return this._realX != this._x || this._realY != this._y;
    }

    isJumping() {
        return this._jumpCount > 0;
    }

    jumpHeight() {
        return (this._jumpPeak * this._jumpPeak -
                Math.pow(this._jumpCount - this._jumpPeak, 2)) / 2;
    }

    isStopping() {
        return !this.isMoving() && !this.isJumping();
    }

    checkStop(threshold) {
        return this._stopCount > threshold;
    }

    resetStopCount() {
        this._stopCount = 0;
    }

    realMoveSpeed() {
        return this._moveSpeed + (this.isDashing() ? 1 : 0);
    }

    distancePerFrame() {
        return Math.pow(2, this.realMoveSpeed()-8);
    }

    isDashing() {
        return false;
    }

    isDebugThrough() {
        return false;
    }

    straighten() {
        if (this.hasWalkAnime() || this.hasStepAnime()) {
            this.setPattern(1);
        }
        this._animationCount = 0;
    }

    reverseDir(d) {
        return 10 - d;
    }

    canPass(x, y, d) {
        let x2 = $gameMap.roundXWithDirection(x, d);
        let y2 = $gameMap.roundYWithDirection(y, d);
        if (!$gameMap.isValid(x2, y2)) {
            return false;
        }
        if (this.isThrough() || this.isDebugThrough()) {
            return true;
        }
        if (!this.isMapPassable(x, y, d)) {
            return false;
        }
        if (this.isCollidedWithCharacters(x2, y2)) {
            return false;
        }
        return true;
    }

    canPassDiagonally(x, y, horz, vert) {
        let x2 = $gameMap.roundXWithDirection(x, horz);
        let y2 = $gameMap.roundYWithDirection(y, vert);
        if (this.canPass(x, y, vert) && this.canPass(x, y2, horz)) {
            return true;
        }
        if (this.canPass(x, y, horz) && this.canPass(x2, y, vert)) {
            return true;
        }
        return false;
    }

    isMapPassable(x, y, d) {
        let x2 = $gameMap.roundXWithDirection(x, d);
        let y2 = $gameMap.roundYWithDirection(y, d);
        let d2 = this.reverseDir(d);
        return $gameMap.isPassable(x, y, d) && $gameMap.isPassable(x2, y2, d2);
    }

    isCollidedWithCharacters(x, y) {
        return this.isCollidedWithEvents(x, y) || this.isCollidedWithVehicles(x, y);
    }

    isCollidedWithEvents(x, y) {
        let events = $gameMap.eventsXyNt(x, y);
        return events.some(event =>event.isNormalPriority());
    }

    isCollidedWithVehicles(x, y) {
        return $gameMap.boat().posNt(x, y) || $gameMap.ship().posNt(x, y);
    }

    copyPosition(character) {
        this._x = character._x;
        this._y = character._y;
        this.setRealPosition(character._realX,character.realY);
        this.setDirection(character._direction);
    }

    locate(x, y) {
        this.setPosition(x, y);
        this.straighten();
        this.refreshBushDepth();
    }

    direction() {
        return this._direction;
    }

    isTile() {
        return this._tileId > 0 && this._priorityType == 0;
    }

    isObjectCharacter() {
        return this._isObjectCharacter;
    }

    shiftY() {
        return this.isObjectCharacter() ? 0 : 6;
    }

    scrolledX() {
        return $gameMap.adjustX(this._realX);
    }

    scrolledY() {
        return $gameMap.adjustY(this._realY);
    }

    screenX() {
        let tw = $gameMap.tileWidth();
        return Math.round(this.scrolledX() * tw + tw / 2);
    }

    screenY() {
        let th = $gameMap.tileHeight();
        return Math.round(this.scrolledY() * th + th -
                          this.shiftY() - this.jumpHeight());
    }

    screenZ() {
        return this._priorityType * 2 + 1;
    }

    isNearTheScreen() {
        let gw = config.width;
        let gh = config.height;
        let tw = $gameMap.tileWidth();
        let th = $gameMap.tileHeight();
        let px = this.scrolledX() * tw + tw / 2 - gw / 2;
        let py = this.scrolledY() * th + th / 2 - gh / 2;
        return px >= -gw && px <= gw && py >= -gh && py <= gh;
    }

    update() {
        if (this.isStopping()) {
            this.updateStop();
        } else if (this.isJumping()) {
            this.updateJump();
        } else if (this.isMoving()) {
            this.updateMove();
        }
        this.updateAnimation();
    }

    updateStop() {
        this._stopCount++;
    }

    updateJump() {
        this._jumpCount--;
        let realX=this._realX;
        let realY=this._realY;
        realX = (realX * this._jumpCount + this._x) / (this._jumpCount + 1.0);
        realY = (realY * this._jumpCount + this._y) / (this._jumpCount + 1.0);
        this.refreshBushDepth();
        if (this._jumpCount == 0) {
            realX = this._x = $gameMap.roundX(this._x);
            realY = this._y = $gameMap.roundY(this._y);
        }
        this.setRealPosition(realX,realY);
    }

    updateMove() {
        let realX=this._realX;
        let realY=this._realY;
        if (this._x < realX) {
            realX = Math.max(realX - this._distancePerFrame, this._x);
        }else if (this._x > realX) {
            realX = Math.min(realX + this._distancePerFrame, this._x);
        }
        if (this._y < realY) {
            realY = Math.max(realY - this._distancePerFrame, this._y);
        }else if (this._y > realY) {
            realY = Math.min(realY + this._distancePerFrame, this._y);
        }
        if (!this.isMoving()) {
            this.refreshBushDepth();
        }
        this.setRealPosition(realX,realY);
    }

    updateAnimation() {
        this.updateAnimationCount();
        if (this._animationCount >= this.animationWait()) {
            this.updatePattern();
            this._animationCount = 0;
        }
    }

    animationWait() {
        return (9 - this.realMoveSpeed()) * 3;
    }

    updateAnimationCount() {
        if (this.isMoving() && this.hasWalkAnime()) {
            this._animationCount += 1.5;
        } else if (this.hasStepAnime() || !this.isOriginalPattern()) {
            this._animationCount++;
        }
    }

    updatePattern() {
        if (!this.hasStepAnime() && this._stopCount > 0) {
            this.resetPattern();
        } else {
            this.setPattern((this._pattern+1)% this.maxPattern());
        }
    }

    maxPattern() {
        return 4;
    }

    pattern() {
        return this._pattern < 3 ? this._pattern : 1;
    }

    isOriginalPattern() {
        return this.pattern() == 1;
    }

    resetPattern() {
        this.setPattern(1);
    }

    refreshBushDepth() {
        if (this.isNormalPriority() 
        && !this.isObjectCharacter() 
        && this.isOnBush() 
        && !this.isJumping()) {
            if (!this.isMoving()) {
                this.setBushDepth(12);
            }
        } else {
             this.setBushDepth(0);
        }
    }

    isOnLadder() {
        return $gameMap.isLadder(this._x, this._y);
    }

    isOnBush() {
        return $gameMap.isBush(this._x, this._y);
    }

    terrainTag() {
        return $gameMap.terrainTag(this._x, this._y);
    }

    regionId() {
        return $gameMap.regionId(this._x, this._y);
    }

    increaseSteps() {
        if (this.isOnLadder()) {
            this.setDirection(8);
        }
        this.resetStopCount();
        this.refreshBushDepth();
    }

    tileId() {
        return this._tileId;
    }

    characterName() {
        return this._characterName;
    }

    characterIndex() {
        return this._characterIndex;
    }

    checkEventTriggerTouchFront(d) {
        let x2 = $gameMap.roundXWithDirection(this._x, d);
        let y2 = $gameMap.roundYWithDirection(this._y, d);
        this.checkEventTriggerTouch(x2, y2);
    }

    checkEventTriggerTouch(x, y) {
        return false;
    }

    isMovementSucceeded(x, y) {
        return this._movementSuccess;
    }

    setMovementSuccess(success) {
        this._movementSuccess = success;
    }

    moveStraight(d) {
        this.setMovementSuccess(this.canPass(this._x, this._y, d));
        if (this.isMovementSucceeded()) {
            this.setDirection(d);
            this._x = $gameMap.roundXWithDirection(this._x, d);
            this._y = $gameMap.roundYWithDirection(this._y, d);
            this._realX = $gameMap.xWithDirection(this._x, this.reverseDir(d));
            this._realY = $gameMap.yWithDirection(this._y, this.reverseDir(d));
            this.increaseSteps();
        } else {
            this.setDirection(d);
            this.checkEventTriggerTouchFront(d);
        }
    }

    moveDiagonally(horz, vert) {
        this.setMovementSuccess(this.canPassDiagonally(this._x, this._y, horz, vert));
        if (this.isMovementSucceeded()) {
            this._x = $gameMap.roundXWithDirection(this._x, horz);
            this._y = $gameMap.roundYWithDirection(this._y, vert);
            this._realX = $gameMap.xWithDirection(this._x, this.reverseDir(horz));
            this._realY = $gameMap.yWithDirection(this._y, this.reverseDir(vert));
            this.increaseSteps();
        }
        if (this._direction == this.reverseDir(horz)) {
            this.setDirection(horz);
        }
        if (this._direction == this.reverseDir(vert)) {
            this.setDirection(vert);
        }
    }

    jump(xPlus, yPlus) {
        if (Math.abs(xPlus) > Math.abs(yPlus)) {
            if (xPlus != 0) {
                this.setDirection(xPlus < 0 ? 4 : 6);
            }
        } else {
            if (yPlus != 0) {
                this.setDirection(yPlus < 0 ? 8 : 2);
            }
        }
        this._x += xPlus;
        this._y += yPlus;
        let distance = Math.round(Math.sqrt(xPlus * xPlus + yPlus * yPlus));
        this._jumpPeak = 10 + distance - this._moveSpeed;
        this._jumpCount = this._jumpPeak * 2;
        this.resetStopCount();
        this.straighten();
        this.emit('character:bus','jump');
    }

    hasWalkAnime() {
        return this._walkAnime;
    }

    setWalkAnime(walkAnime) {
        this._walkAnime = walkAnime;
    }

    hasStepAnime() {
        return this._stepAnime;
    }

    setStepAnime(stepAnime) {
        this._stepAnime = stepAnime;
    }

    isDirectionFixed() {
        return this._directionFix;
    }

    setDirectionFix(directionFix) {
        this._directionFix = directionFix;
    }

    isThrough() {
        return this._through;
    }

    setThrough(through) {
        this._through = through;
    }

    isTransparent() {
        return this._transparent;
    }

    bushDepth() {
        return this._bushDepth;
    }

    animationId() {
        return this._animationId;
    }

    balloonId() {
        return this._balloonId;
    }

    startAnimation() {
        this._animationId = 0;
        this._animationPlaying = true;
    }

    startBalloon() {
        this._balloonId = 0;
        this._balloonPlaying = true;
    }

    isAnimationPlaying() {
        return this._animationId > 0 || this._animationPlaying;
    }

    isBalloonPlaying() {
        return this._balloonId > 0 || this._balloonPlaying;
    }

    endAnimation() {
        this._animationPlaying = false;
    }

    endBalloon() {
        this._balloonPlaying = false;
    }

    serialize(){
        return {
            x: this._x,
            y: this._y,
            realX: this._realX,
            realY: this._realY,
            moveSpeed: this._moveSpeed,
            moveFrequency: this._moveFrequency,
            opacity: this._opacity,
            blendMode: this._blendMode,
            direction: this._direction,
            pattern: this._pattern,
            priorityType: this._priorityType,
            tileId: this._tileId,
            characterName: this._characterName,
            characterIndex: this._characterIndex,
            isObjectCharacter: this._isObjectCharacter,
            walkAnime: this._walkAnime,
            stepAnime: this._stepAnime,
            directionFix: this._directionFix,
            through: this._through,
            transparent: this._transparent,
            bushDepth: this._bushDepth,
            animationId: this._animationId,
            balloonId: this._balloonId,
            animationPlaying: this._animationPlaying,
            balloonPlaying: this._balloonPlaying,
            animationCount: this._animationCount,
            stopCount: this._stopCount,
            jumpCount: this._jumpCount,
            jumpPeak: this._jumpPeak,
            movementSuccess: this._movementSuccess
        };
    }

    deserialize(data){
        this._x = data.x;
        this._y = data.y;
        this._realX = data.realX;
        this._realY = data.realY;
        this._moveSpeed = data.moveSpeed;
        this._moveFrequency = data.moveFrequency;
        this._opacity = data.opacity;
        this._blendMode = data.blendMode;
        this._direction = data.direction;
        this._pattern = data.pattern;
        this._priorityType = data.priorityType;
        this._tileId = data.tileId;
        this._characterName = data.characterName;
        this._characterIndex = data.characterIndex;
        this._isObjectCharacter = data.isObjectCharacter;
        this._walkAnime = data.walkAnime;
        this._stepAnime = data.stepAnime;
        this._directionFix = data.directionFix;
        this._through = data.through;
        this._transparent = data.transparent;
        this._bushDepth = data.bushDepth;
        this._animationId = data.animationId;
        this._balloonId = data.balloonId;
        this._animationPlaying = data.animationPlaying;
        this._balloonPlaying = data.balloonPlaying;
        this._animationCount = data.animationCount;
        this._stopCount = data.stopCount;
        this._jumpCount = data.jumpCount;
        this._jumpPeak = data.jumpPeak;
        this._movementSuccess = data.movementSuccess;
    }
}